#region Licence...

//-----------------------------------------------------------------------------
// Date:	25/10/10
// Module:	Utils.cs
// Classes:	...
//
// This module contains the definition of the utility classes used by CS-Script modules
//
// Written by Oleg Shilo (oshilo@gmail.com)
// Copyright (c) 2004-2011. All rights reserved.
//
// Redistribution and use of this code WITHOUT MODIFICATIONS are permitted provided that
// the following conditions are met:
// 1. Redistributions must retain the above copyright notice, this list of conditions
//  and the following disclaimer.
// 2. Neither the name of an author nor the names of the contributors may be used
//	to endorse or promote products derived from this software without specific
//	prior written permission.
//
// Redistribution and use of this code WITH MODIFICATIONS are permitted provided that all
// above conditions are met and software is not used or sold for profit.
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//	Caution: Bugs are expected!
//----------------------------------------------

#endregion Licence...

using System;
using System.IO;
using System.Reflection;

#if net1
using System.Collections;
#else

using System.Collections.Generic;

#endif

using System.Text;
using CSScriptLibrary;
using System.Runtime.InteropServices;
using System.CodeDom.Compiler;

namespace csscript
{
    class CurrentDirGuard : IDisposable
    {
        string currentDir = Environment.CurrentDirectory;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
                Environment.CurrentDirectory = currentDir;

            disposed = true;
        }

        ~CurrentDirGuard()
        {
            Dispose(false);
        }

        bool disposed = false;
    }

    class Utils
    {
        //unfortunately LINQ is not available for .NET 1.1 compilations
        public static string[] Concat(string[] array1, string[] array2)
        {
            string[] retval = new string[array1.Length + array2.Length];
            Array.Copy(array1, 0, retval, 0, array1.Length);
            Array.Copy(array2, 0, retval, array1.Length, array2.Length);
            return retval;
        }

        public static string[] Concat(string[] array1, string item)
        {
            string[] retval = new string[array1.Length + 1];
            Array.Copy(array1, 0, retval, 0, array1.Length);
            retval[retval.Length - 1] = item;
            return retval;
        }

        public static string[] RemoveDuplicates(string[] list)
        {
            System.Collections.ArrayList retval = new System.Collections.ArrayList();
            foreach (string item in list)
            {
                if (item.Trim() == "")
                {
                    retval.Add(item);
                }
                else
                {
                    string path = Path.GetFullPath(item).ToLower();
                    if (!retval.Contains(path))
                        retval.Add(path);
                }
            }

            return (string[])retval.ToArray(typeof(string));
        }

        public static string[] RemoveEmptyStrings(string[] list)
        {
            System.Collections.ArrayList retval = new System.Collections.ArrayList();
            foreach (string item in list)
            {
                if (item.Trim() != "")
                    retval.Add(item);
            }

            return (string[])retval.ToArray(typeof(string));
        }

        /// <summary>
        /// Adds compiler options to the CompilerParameters in a maner that it does separete every option by the space character
        /// </summary>
        static public void AddCompilerOptions(CompilerParameters compilerParams, string option)
        {
            compilerParams.CompilerOptions += option + " ";
        }

        ///// <summary>
        ///// More reliable version of the Path.GetTempFileName().
        ///// It is required because it was some reports about non unique names returned by Path.GetTempFileName()
        ///// when running in multi-threaded environment.
        ///// (it is not used yet as I did not give up on PInvoke GetTempFileName())
        ///// </summary>
        ///// <returns>Temporary file name.</returns>
        //string PathGetTempFileName()
        //{
        //    return Path.GetTempPath() + Guid.NewGuid().ToString() + ".tmp";
        //}
    }

    class CSSUtils
    {
        internal static string GetScriptedCodeAttributeInjectionCode()
        {
            string code = "[assembly: CSScriptLibrary.ScriptedCode]";
            string currentCode = "";
            string file = Path.Combine(CSExecutor.GetScriptTempDir(), @"CodeIjection\ScriptedCodeAttributeInjection.cs");

            if (File.Exists(file))
                using (StreamReader sr = new StreamReader(file))
                    currentCode = sr.ReadToEnd();

            if (currentCode != code)
            {
                string dir = Path.GetDirectoryName(file);
                if (!Directory.Exists(dir))
                    Directory.CreateDirectory(dir);

                using (StreamWriter sw = new StreamWriter(file))
                {
                    sw.Write(code);
                }
            }
            return file;
        }

        public static bool HaveSameTimestamp(string file1, string file2)
        {
            FileInfo info1 = new FileInfo(file1);
            FileInfo info2 = new FileInfo(file2);

            return (info2.LastWriteTime == info1.LastWriteTime &&
                    info2.LastWriteTimeUtc == info1.LastWriteTimeUtc);
        }

        public static void SetTimestamp(string fileDest, string fileSrc)
        {
            FileInfo info1 = new FileInfo(fileSrc);
            FileInfo info2 = new FileInfo(fileDest);

            info2.LastWriteTime = info1.LastWriteTime;
            info2.LastWriteTimeUtc = info1.LastWriteTimeUtc;
        }

        public delegate void ShowDocumentHandler();

        /// <summary>
        /// Parses application (script engine) arguments.
        /// </summary>
        /// <param name="args">Arguments</param>
        /// <param name="executor">Script executor instance</param>
        /// <returns>Index of the first script argument</returns>
        static internal int ParseAppArgs(string[] args, IScriptExecutor executor)
        {
            ExecuteOptions options =executor.GetOptions();
            //Debug.Assert(false);
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i].StartsWith("/"))
                {
                    if (args[i] == "/nl")
                    {
                        options.noLogo = true;
                    }
                    else if (args[i] == "/c" && (!options.supressExecution))
                    {
                        options.useCompiled = true;
                    }
                    else if (args[i] == "/sconfig")
                    {
                        options.useScriptConfig = true;
                    }
                    else if (args[i].StartsWith("/sconfig:"))
                    {
                        options.useScriptConfig = true;
                        options.customConfigFileName = args[i].Substring("/sconfig:".Length);
                    }
                    else if (args[i] == "/verbose")
                    {
                        options.verbose = true;
                    }
                    else if (args[i].StartsWith("/dir:"))
                    {
                        foreach (string dir in args[i].Substring("/dir:".Length).Split(','))
                            options.AddSearchDir(dir.Trim());
                    }
                    //else if (args[i].StartsWith("/nohost"))
                    //{
                    //    options.suppressExternalHosting = true;
                    //}
                    else if (args[i].StartsWith("/noconfig"))
                    {
                        options.noConfig = true;
                        if (args[i].StartsWith("/noconfig:"))
                            options.altConfig = args[i].Substring("/noconfig:".Length);
                    }
                    else if (args[i] == "/autoclass" || args[i] == "/ac")
                    {
                        options.autoClass = true;
                    }
                    else if (args[i].StartsWith("/ca"))
                    {
                        options.useCompiled = true;
                        options.forceCompile = true;
                        options.supressExecution = true;
                    }
                    else if (args[i].StartsWith("/co:"))
                    {
                        options.compilerOptions = args[i].Substring("/co:".Length);
                    }
                    else if (args[i].StartsWith("/cd"))
                    {
                        options.supressExecution = true;
                        options.DLLExtension = true;
                    }
                    else if (args[i] == "/dbg" || args[i] == "/d")
                    {
                        options.DBG = true;
                    }
                    else if (args[i] == "/l")
                    {
                        options.local = true;
                    }
                    else if (args[i].StartsWith("/r:"))
                    {
                        string[] assemblies = args[i].Remove(0, 3).Split(":".ToCharArray());
                        options.refAssemblies = assemblies;
                    }
                    else if (args[i].StartsWith("/e") && !options.buildExecutable)
                    {
                        options.buildExecutable = true;
                        options.supressExecution = true;
                        options.buildWinExecutable = args[i].StartsWith("/ew");
                    }
                    else if (args[0] == "/?" || args[0] == "-?")
                    {
                        executor.ShowHelp();
                        options.processFile = false;
                        break;
                    }
                    else if (args[0] == "/s")
                    {
                        executor.ShowSample();
                        options.processFile = false;
                        break;
                    }
                }
                else
                {
                    return i;
                }
            }
            return args.Length;
        }

        public static string GenerateAutoclass(string file)
        {
            StringBuilder code = new StringBuilder(4096);
            code.Append("//Auto-generated file\r\n"); //cannot use AppendLine as it is not available in StringBuilder v1.1
            //code.Append("using System;\r\n");

            bool headerProcessed = false;
            string line;
            using (StreamReader sr = new StreamReader(file, Encoding.Default))
                while ((line = sr.ReadLine()) != null)
                {
                    if (!headerProcessed && !line.TrimStart().StartsWith("using ")) //not using...; statement of the file header
                        if (!line.StartsWith("//") && line.Trim() != "") //not comments or empty line
                        {
                            headerProcessed = true;
                            //code.Append("namespace Scripting\r\n");
                            //code.Append("{\r\n");
                            code.Append("   public class ScriptClass\r\n");
                            code.Append("   {\r\n");
                            code.Append("   static public ");
                        }

                    code.Append(line);
                    code.Append("\r\n");
                }

            code.Append("   }\r\n");
            //code.Append("}\r\n");

            string autogenFile = Path.Combine(CSExecutor.GetCacheDirectory(file), Path.GetFileNameWithoutExtension(file) + ".g" + Path.GetExtension(file));
            using (StreamWriter sw = new StreamWriter(autogenFile, false, Encoding.Default))
                sw.Write(code.ToString());

            return autogenFile;
        }
    }



    #region MetaDataItems...

    /// <summary>
    /// The MetaDataItems class contains information about script dependencies (referenced local
    /// assemblies and imported scripts) and copmpiler options. This information is required when
    /// scripts are executed in a 'cached' mode (/c switch). On the base of this information the script
    /// engine will compile new version of .csc assembly if any of it's dependencies is changed. This
    /// is required even for referenced local assemblies as it is possible that they are a strongly
    /// named assemblies (recompiling is required for any compiled client of the strongly named assembly
    /// in case this assembly is changed).
    ///
    /// The perfect place to store the dependencies info (custom meta data) is the assembly
    /// resources. However if we do so such assemblies would have to be loaded in order to read their
    /// resources. It is not acceptable as after loading assembly cannot be unloaded. Also assembly loading
    /// can significantly compromise performance.
    ///
    /// That is why custom meta data is just physically appended to the file. This is a valid
    /// approach because such assembly is not to be distributed anywhere but to stay always
    /// on the PC and play the role of the temporary data for the script engine.
    ///
    /// Note: A .dll assembly is always compiled and linked in a normal way without any custom meta data attached.
    /// </summary>
    class MetaDataItems
    {
        public class MetaDataItem
        {
            public MetaDataItem(string file, DateTime date, bool assembly)
            {
                this.file = file;
                this.date = date;
                this.assembly = assembly;
            }

            public string file;
            public DateTime date;
            public bool assembly;
        }

#if net1
        public ArrayList items = new ArrayList();
#else
        public List<MetaDataItem> items = new List<MetaDataItem>();
#endif

        static public bool IsOutOfDate(string script, string assembly)
        {
            MetaDataItems depInfo = new MetaDataItems();

            if (depInfo.ReadFileStamp(assembly))
            {
                //Trace.WriteLine("Reading mete data...");
                //foreach (MetaDataItems.MetaDataItem item in depInfo.items)
                //    Trace.WriteLine(item.file + " : " + item.date);

                string dependencyFile = "";
                foreach (MetaDataItem item in depInfo.items)
                {
                    if (item.assembly)
                    {
                        if (Path.IsPathRooted(item.file)) //is absolute path
                        {
                            dependencyFile = item.file;
                        }
                        else
                        {
                            foreach (string dir in CSExecutor.options.searchDirs)
                            {
                                dependencyFile = Path.Combine(dir, item.file); //assembly should be in the same directory with the script
                                if (File.Exists(dependencyFile))
                                    break;
                            }
                        }
                    }
                    else
                        dependencyFile = FileParser.ResolveFile(item.file, CSExecutor.options.searchDirs, false);

                    if (!File.Exists(dependencyFile) || File.GetLastWriteTimeUtc(dependencyFile) != item.date)
                    {
                        return true;
                    }
                }
                return false;
            }
            else
                return true;
        }

        public string[] AddItems(System.Collections.Specialized.StringCollection files, bool isAssembly, string[] searchDirs)
        {
            string[] referencedAssemblies = new string[files.Count];
            files.CopyTo(referencedAssemblies, 0);
            return AddItems(referencedAssemblies, isAssembly, searchDirs);
        }

        public string[] AddItems(string[] files, bool isAssembly, string[] searchDirs)
        {
#if net1
            ArrayList newProbingDirs = new ArrayList();
#else
            List<string> newProbingDirs = new List<string>();
#endif
            if (isAssembly)
            {
                foreach (string asmFile in files)
                {
                    //under some conditions assemblies do not have a location (e.g. dynamically built/emitted assemblies under ASP.NET)
                    if (!File.Exists(asmFile))
                        continue;

                    try
                    {
                        if (!IsGACAssembly(asmFile))
                        {
                            bool found = false;
                            foreach (string dir in searchDirs)
                                if (!IsGACAssembly(asmFile) && string.Compare(dir, Path.GetDirectoryName(asmFile), true) == 0)
                                {
                                    found = true;
                                    AddItem(Path.GetFileName(asmFile), File.GetLastWriteTimeUtc(asmFile), true);
                                    break;
                                }

                            if (!found) //the assembly was not in the search dirs
                            {
                                newProbingDirs.Add(Path.GetDirectoryName(asmFile));
                                AddItem(asmFile, File.GetLastWriteTimeUtc(asmFile), true); //assembly from the absolute path
                            }
                        }
                    }
                    catch (NotSupportedException)
                    {
                        //under ASP.NET some assemblies do not have location (e.g. dynamically built/emitted assemblies)
                    }
                    catch (ArgumentException)
                    {
                        //The asm.location parameter contains invalid characters, is empty, or contains only white spaces, or contains a wildcard character
                    }
                    catch (PathTooLongException)
                    {
                        //The asm.location parameter is longer than the system-defined maximum length
                    }
                    catch
                    {
                        //In fact ignore all exceptions at we should continue if for what ever reason assembly location cannot be obtained
                    }
                }
            }
            else
            {
                foreach (string file in files)
                {
                    string fullPath = Path.GetFullPath(file);

                    bool local = false;
                    foreach (string dir in searchDirs)
                        if ((local = (string.Compare(dir, Path.GetDirectoryName(fullPath), true) == 0)))
                            break;

                    if (local)
                        AddItem(Path.GetFileName(file), File.GetLastWriteTimeUtc(file), false);
                    else
                        AddItem(file, File.GetLastWriteTimeUtc(file), false);
                }
            }
#if net1
            return (string[])newProbingDirs.ToArray(typeof(string));
#else
            return newProbingDirs.ToArray();
#endif
        }

        public void AddItem(string file, DateTime date, bool assembly)
        {
            this.items.Add(new MetaDataItem(file, date, assembly));
        }

        public bool StampFile(string file)
        {
            //Trace.WriteLine("Writing mete data...");
            //foreach (MetaDataItem item in items)
            //    Trace.WriteLine(item.file + " : " + item.date);

            try
            {
                using (FileStream fs = new FileStream(file, FileMode.Open))
                {
                    fs.Seek(0, SeekOrigin.End);
                    using (BinaryWriter w = new BinaryWriter(fs))
                    {
                        char[] data = this.ToString().ToCharArray();
                        w.Write(data);
                        w.Write((Int32)data.Length);
                        w.Write((Int32)(CSExecutor.options.DBG ? 1 : 0));
                        w.Write((Int32)Environment.Version.ToString().GetHashCode());
                        w.Write((Int32)stampID);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                return false;
            }
            return true;
        }

        public bool ReadFileStamp(string file)
        {
            try
            {
                using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
                {
                    using (BinaryReader r = new BinaryReader(fs))
                    {
                        fs.Seek(-intSize, SeekOrigin.End);
                        int stamp = r.ReadInt32();
                        if (stamp == stampID)
                        {
                            fs.Seek(-(intSize * 2), SeekOrigin.End);
                            if (r.ReadInt32() != Environment.Version.ToString().GetHashCode())
                            {
                                //Console.WriteLine("Environment.Version");
                                return false;
                            }

                            fs.Seek(-(intSize * 3), SeekOrigin.End);
                            if (r.ReadInt32() != (CSExecutor.options.DBG ? 1 : 0))
                            {
                                //Console.WriteLine("CSExecutor.options.DBG");
                                return false;
                            }

                            fs.Seek(-(intSize * 4), SeekOrigin.End);
                            int dataSize = r.ReadInt32();

                            if (dataSize != 0)
                            {
                                fs.Seek(-(intSize * 4 + dataSize), SeekOrigin.End);
                                return this.Parse(new string(r.ReadChars(dataSize)));
                            }
                            else
                                return true;
                        }
                        return false;
                    }
                }
            }
            catch
            {
            }
            return false;
        }

        private new string ToString()
        {
            StringBuilder bs = new StringBuilder();
            foreach (MetaDataItem fileInfo in items)
            {
                bs.Append(fileInfo.file);
                bs.Append(";");
                bs.Append(fileInfo.date.ToFileTimeUtc().ToString());
                bs.Append(";");
                bs.Append(fileInfo.assembly ? "Y" : "N");
                bs.Append("|");
            }
            return bs.ToString();
        }

        private bool Parse(string data)
        {
            foreach (string itemData in data.Split("|".ToCharArray()))
            {
                if (itemData.Length > 0)
                {
                    string[] parts = itemData.Split(";".ToCharArray());
                    if (parts.Length == 3)
                    {
                        this.items.Add(new MetaDataItem(parts[0], DateTime.FromFileTimeUtc(long.Parse(parts[1])), parts[2] == "Y"));
                    }
                    else
                        return false;
                }
            }
            return true;
        }

        private int stampID = Assembly.GetExecutingAssembly().FullName.Split(",".ToCharArray())[1].GetHashCode();
        private int intSize = Marshal.SizeOf((Int32)0);

        //#pragma warning disable 414
        private int executionFlag = Marshal.SizeOf((Int32)0);
        //#pragma warning restore 414

        bool IsGACAssembly(string file)
        {
            string s = file.ToLower();
#if net1
            return s.IndexOf("microsoft.net\\framework") != -1 || s.IndexOf("microsoft.net/framework") != -1 || s.IndexOf("gac_msil") != -1 || s.IndexOf("gac_64") != -1 || s.IndexOf("gac_32") != -1;
#else
            return s.Contains("microsoft.net\\framework") || s.Contains("microsoft.net/framework") || s.Contains("gac_msil") || s.Contains("gac_64") || s.Contains("gac_32");
#endif
        }
    }

    #endregion MetaDataItems...

    class HelpProvider
    {
        public static string BuildCommandInterfaceHelp()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(AppInfo.appLogo);
            builder.Append("\nUsage: " + AppInfo.appName + " <switch 1> <switch 2> <file> [params] [//x]\n");
            builder.Append("\n");
            builder.Append("<switch 1>\n");
            builder.Append(" /?    - Display help info.\n");
            builder.Append(" /e    - Compile script into console application executable.\n");
            builder.Append(" /ew   - Compile script into Windows application executable.\n");
            builder.Append(" /c    - Use compiled file (cache file .csc) if found (to improve performance).\n");
            builder.Append(" /ca   - Compile script file into assembly (cache file .csc) without execution.\n");
            builder.Append(" /cd   - Compile script file into assembly (.dll) without execution.\n\n");
            builder.Append(" /co:<options>\n");
            builder.Append("       - Pass compiler options directly to the language compiler\n");
            builder.Append("       (e.g. /co:/d:TRACE pass /d:TRACE option to C# compiler\n");
            builder.Append("        or /co:/platform:x86 to produce Win32 executable)\n\n");
            builder.Append(" /s    - Print content of sample script file (e.g. " + AppInfo.appName + " /s > sample.cs).\n");
            builder.Append(" /ac | /autoclass\n");
            builder.Append("       - Automatically generates wrapper class if the script does not define any class of its own:\n");
            builder.Append("\n");
            builder.Append("         using System;\n");
            builder.Append("                      \n");
            builder.Append("         void Main()\n");
            builder.Append("         {\n");
            builder.Append("             Console.WriteLine(\"Hello World!\");\n");
            builder.Append("         }\n");
            builder.Append("\n");
            builder.Append("\n");
            builder.Append("<switch 2>\n");
            if (AppInfo.appParamsHelp != "")
                builder.Append(" /" + AppInfo.appParamsHelp);	//application specific usage info
            builder.Append(" /dbg | /d\n");
            builder.Append("       - Force compiler to include debug information.\n");
            builder.Append(" /l    - 'local'(makes the script directory a 'current directory')\n");
            builder.Append(" /verbose \n");
            builder.Append("       - prints runtime information during the script execution (applicable for console clients only)\n");
            builder.Append(" /noconfig[:<file>]\n       - Do not use default CS-Script config file or use alternative one.\n");
            builder.Append("         (e.g. " + AppInfo.appName + " /noconfig sample.cs\n");
            builder.Append("              " + AppInfo.appName + " /noconfig:c:\\cs-script\\css_VB.dat sample.vb)\n");
            builder.Append(" /sconfig[:<file>]\n       - Use script config file or custom config file as a .NET application configuration file.\n");
            builder.Append("This option might be useful for running scripts, which usually cannot be executed without configuration file (e.g. WCF, Remoting).\n\n");
            builder.Append("          (e.g. if /sconfig is used the expected config file name is <script_name>.cs.config or <script_name>.exe.config\n");
            builder.Append("           if /sconfig:myApp.config is used the expected config file name is myApp.config\n)");
            builder.Append(" /r:<assembly 1>:<assembly N>\n");
            builder.Append("       - Use explicitly referenced assembly. It is required only for\n");
            builder.Append("         rare cases when namespace cannot be resolved into assembly.\n");
            builder.Append("         (e.g. " + AppInfo.appName + " /r:myLib.dll myScript.cs).\n");
            builder.Append(" /dir:<directory 1>,<directory N>\n");
            builder.Append("       - Add path(s) to the assembly probing directory list.\n");
            builder.Append("         (e.g. " + AppInfo.appName + " /dir:C:\\MyLibraries myScript.cs).\n");
            builder.Append("\n");
            builder.Append("file   - Specifies name of a script file to be run.\n");
            builder.Append("params - Specifies optional parameters for a script file to be run.\n");
            builder.Append(" //x   - Launch debugger just before starting the script.\n");
            builder.Append("\n");
            if (AppInfo.appConsole) // a temporary hack to prevent showing a huge message box when not in console mode
            {
                builder.Append("\n");
                builder.Append("**************************************\n");
                builder.Append("Script specific syntax\n");
                builder.Append("**************************************\n");
                builder.Append("\n");
                builder.Append("Engine directives:\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_import <file>[, preserve_main][, rename_namespace(<oldName>, <newName>)];\n");
                builder.Append("\n");
                builder.Append("Alias - //css_imp\n");
                builder.Append("There are also another two aliases //css_include and //css_inc. They are equivalents of //css_import <file>, preserve_main\n");
                builder.Append("If $this (or $this.name) is specified as part of <file> it will be replaced at execution time with the main script full name (or file name only).\n");
                builder.Append("\n");
                builder.Append("file            - name of a script file to be imported at compile-time.\n");
                builder.Append("<preserve_main> - do not rename 'static Main'\n");
                builder.Append("oldName         - name of a namespace to be renamed during importing\n");
                builder.Append("newName         - new name of a namespace to be renamed during importing\n");
                builder.Append("\n");
                builder.Append("This directive is used to inject one script into another at compile time. Thus code from one script can be exercised in another one.\n");
                builder.Append("'Rename' clause can appear in the directive multiple times.\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_args arg0[,arg1]..[,argN];\n");
                builder.Append("\n");
                builder.Append("Embedded script arguments. The both script and engine arguments are allowed except \"/noconfig\" engine command switch.\n");
                builder.Append(" Example: //css_args /dbg;\n This directive will always force script engine to execute the script in debug mode.\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_reference <file>;\n");
                builder.Append("\n");
                builder.Append("Alias - //css_ref\n");
                builder.Append("\n");
                builder.Append("file	- name of the assembly file to be loaded at run-time.\n");
                builder.Append("\n");
                builder.Append("This directive is used to reference assemblies required at run time.\n");
                builder.Append("The assembly must be in GAC, the same folder with the script file or in the 'Script Library' folders (see 'CS-Script settings').\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_searchdir <directory>;\n");
                builder.Append("\n");
                builder.Append("Alias - //css_dir\n");
                builder.Append("\n");
                builder.Append("directory - name of the directory to be used for script and assembly probing at run-time.\n");
                builder.Append("\n");
                builder.Append("This directive is used to extend set of search directories (script and assembly probing).\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_resource <file>;\n");
                builder.Append("\n");
                builder.Append("Alias - //css_res\n");
                builder.Append("\n");
                builder.Append("file	- name of the resource file (.resources) to be used with the script.\n");
                builder.Append("\n");
                builder.Append("This directive is used to reference resource file for script.\n");
                builder.Append(" Example: //css_res Scripting.Form1.resources;\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_co <options>;\n");
                builder.Append("\n");
                builder.Append("options - options string.\n");
                builder.Append("\n");
                builder.Append("This directive is used to pass compiler options string directly to the language specific CLR compiler.\n");
                builder.Append(" Example: //css_co /d:TRACE pass /d:TRACE option to C# compiler\n");
                builder.Append("          //css_co /platform:x86 to produce Win32 executable\n\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_ignore_namespace <namespace>;\n");
                builder.Append("\n");
                builder.Append("Alias - //css_ignore_ns\n");
                builder.Append("\n");
                builder.Append("namespace	- name of the namespace.\n");
                builder.Append("\n");
                builder.Append("This directive is used to prevent CS-Script from resolving the referenced namespace into assembly.\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_prescript file([arg0][,arg1]..[,argN])[ignore];\n");
                builder.Append("//css_postscript file([arg0][,arg1]..[,argN])[ignore];\n");
                builder.Append("\n");
                builder.Append("Aliases - //css_pre and //css_post\n");
                builder.Append("\n");
                builder.Append("file    - script file (extension is optional)\n");
                builder.Append("arg0..N - script string arguments\n");
                builder.Append("ignore  - continue execution of the main script in case of error\n");
                builder.Append("\n");
                builder.Append("These directives are used to execute secondary pre- and post-action scripts.\n");
                builder.Append("If $this (or $this.name) is specified as arg0..N it will be replaced at execution time with the main script full name (or file name only).\n");
                builder.Append("------------------------------------\n");
                builder.Append("//css_host [/version:<CLR_Version>] [/platform:<CPU>]\n");
                builder.Append("\n");
                builder.Append("CLR_Version - version of CLR the script should be execute on (e.g. //css_host /version:v3.5)\n");
                builder.Append("CPU - indicates which platforms the script should be run on: x86, Itanium, x64, or anycpu.\n");
                builder.Append("Sample: //css_host /version:v2.0 /platform:x86;");
                builder.Append("\n");
                builder.Append("These directive is used to execute script from a surrogate host process. The script engine application (cscs.exe or csws.exe) launches the script execution as a separate process of the s are used to execute secondary pre- and post-action scripts.\n");
                builder.Append("execution as a separate process of the specified CLR version and CPU architecture.\n");
                builder.Append("------------------------------------\n");
                builder.Append("\n");
                builder.Append("Any directive has to be written as a single line in order to have no impact on compiling by CLI compliant compiler.\n");
                builder.Append("It also must be placed before any namespace or class declaration.\n");
                builder.Append("\n");
                builder.Append("------------------------------------\n");
                builder.Append("Example:\n");
                builder.Append("\n");
                builder.Append(" using System;\n");
                builder.Append(" //css_prescript com(WScript.Shell, swshell.dll);\n");
                builder.Append(" //css_import tick, rename_namespace(CSScript, TickScript);\n");
                builder.Append(" //css_reference teechart.lite.dll;\n");
                builder.Append(" \n");
                builder.Append(" namespace CSScript\n");
                builder.Append(" {\n");
                builder.Append("   class TickImporter\n");
                builder.Append("   {\n");
                builder.Append("      static public void Main(string[] args)\n");
                builder.Append("      {\n");
                builder.Append("         TickScript.Ticker.i_Main(args);\n");
                builder.Append("      }\n");
                builder.Append("   }\n");
                builder.Append(" }\n");
                builder.Append("\n");
            }

            return builder.ToString();
        }

        public static string BuildSampleCode()
        {
            StringBuilder builder = new StringBuilder();

            builder.Append("using System;\r\n");
            builder.Append("using System.Windows.Forms;\r\n");
            builder.Append("\r\n");
            builder.Append("class Script\r\n");
            builder.Append("{\r\n");
            builder.Append("	[STAThread]\r\n");
            builder.Append("	static public void Main(string[] args)\r\n");
            builder.Append("	{\r\n");
            builder.Append("		MessageBox.Show(\"Just a test!\");\r\n");
            builder.Append("\r\n");
            builder.Append("		for (int i = 0; i < args.Length; i++)\r\n");
            builder.Append("		{\r\n");
            builder.Append("			Console.WriteLine(args[i]);\r\n");
            builder.Append("		}\r\n");
            builder.Append("	}\r\n");
            builder.Append("}\r\n");

            return builder.ToString();
        }

    }
}